/**
 * 
 */
package org.vsg.stock.basic.service.impl;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;

import javax.annotation.Resource;

import org.vsg.common.biz.exception.ServiceException;
import org.vsg.stock.basic.domain.StockBlurRange;
import org.vsg.stock.basic.domain.StockBlurRangePK;
import org.vsg.stock.basic.persistence.StockBlurRangeDao;
import org.vsg.stock.basic.service.StockBusinessHelper;
import org.vsg.stock.core.kline.DailyKLineStock;

/**
 * @author Bill Vison
 *
 */
public class StockBusinessHelperManagerImpl implements
		org.vsg.stock.basic.service.StockBusinessHelperManager {
	
	@Resource
	private StockBlurRangeDao stockBlurRangeDao;
	

	/* (non-Javadoc)
	 * @see org.vsg.stock.basic.service.StockBusinessHelperManager#getStockBlurLimitedPrice(double, org.vsg.stock.basic.service.StockBusinessHelperManager.BlurRangeType, java.lang.String)
	 */
	@Override
	public double getStockBlurLimitedPrice(double price, BlurRangeType type,
			String stockCode) {
		
		Set<StockBlurRange>  allBlurRangeSet =  cacheStockBlurRange( stockCode );
		
		// --- if not find the value , using default
		StockBlurRangePK id = null;
		StockBlurRange selectedblurRange = null;
		
		for (StockBlurRange blurRange : allBlurRangeSet) {
			id = blurRange.getId();
			if (  price <= id.getUpperLimit()  && price > id.getLowerLimit() ) {
				selectedblurRange = blurRange;
				break;
			}
		}
		
		double maxPrice = 0;
		double minPrice = 0;
		try {
			if (selectedblurRange == null) {
				// --- use default value ---
				throw new ServiceException("no select buld range refered to stock ["+stockCode+"]");
			}

			maxPrice = price + price * selectedblurRange.getUpperPrecent() / 100;
			minPrice = price - price * selectedblurRange.getLowerPrecent() / 100;	
			
		} catch (ServiceException se) {
			
			maxPrice = getStockBlurLimitedPrice(price, BlurRangeType.MAX);
			minPrice = getStockBlurLimitedPrice(price, BlurRangeType.MIN);
			
		} 
		
		if (type == BlurRangeType.MAX) {
			return maxPrice;
		} else {
			return minPrice;
		}		

	}

	/**
	 * cache stock blur range --
	 */
	private LinkedHashMap<String, Set<StockBlurRange>> tmpProxy = new LinkedHashMap<String, Set<StockBlurRange>> ();
	
	private Set<StockBlurRange> cacheStockBlurRange(String stockCode ) {
		Set<StockBlurRange>  allBlurRangeSet = tmpProxy.get( stockCode );
		if (allBlurRangeSet == null) {
			allBlurRangeSet =  stockBlurRangeDao.findAllByStock( stockCode );
		}
		tmpProxy.put(stockCode, allBlurRangeSet);
		return allBlurRangeSet;
	}
	
	
	@Override
	public boolean checkBlurLimited(double price, double diff, String stockCode) {

		Set<StockBlurRange>  allBlurRangeSet =  cacheStockBlurRange( stockCode );
		
		// --- if not find the value , using default
		StockBlurRangePK id = null;
		StockBlurRange selectedblurRange = null;
		double compValue = (diff * 100 )/ price ;
		
		for (StockBlurRange blurRange : allBlurRangeSet) {
			id = blurRange.getId();
			if (  price <= id.getUpperLimit()  && price > id.getLowerLimit() ) {
				selectedblurRange = blurRange;
				break;
			}
		}		
		
		double maxPrice = 0;
		double minPrice = 0;
		try {
			if (selectedblurRange == null) {
				// --- use default value ---
				throw new ServiceException("no select buld range refered to stock ["+stockCode+"]");
			}

			maxPrice = price + price * selectedblurRange.getUpperPrecent() / 100;
			minPrice = price + price * selectedblurRange.getLowerPrecent() / 100;	
			
		} catch (ServiceException se) {
			
			maxPrice = getStockBlurLimitedPrice(price, BlurRangeType.MAX);
			minPrice = getStockBlurLimitedPrice(price, BlurRangeType.MIN);
			
		} 
		
		
		// --- stock with low price will be set the higher of the radio than high price
		boolean canMerge = price > minPrice && price <= maxPrice;
		return canMerge;		

	}




	@Override
	public Map<Double, String> mergeToPriceMap(DailyKLineStock kline , String stockCode) throws ServiceException {
		
		if (stockCode == null || stockCode.trim().equals("")) {
			throw new ServiceException("could not defined the stock code");
		}
		
		
		double mainTopPrice = kline.getOpen() > kline.getClose() ? kline.getOpen() : kline.getClose();
		double mainButtonPrice = kline.getClose() < kline.getOpen() ? kline.getClose() : kline.getOpen();
		
		
		Map<Double,String> mergePrice = new WeakHashMap<Double,String>();
		
		// --- difference between high , close , open , low ---
		double diffInHT = Math.abs( kline.getHigh() - mainTopPrice );
		double diffInHB = Math.abs( kline.getHigh() - mainButtonPrice );
		double diffInLB = Math.abs( kline.getLow() - mainButtonPrice );
		double diffInLT = Math.abs( kline.getLow() - mainTopPrice );
		double diffInOC = Math.abs( kline.getOpen() - kline.getClose() );
		
		boolean needRecordHigh = true;
		boolean needRecordOpen = true;
		boolean needRecordClose = true;
		boolean needRecordLow = true;
		
		// --- high check ----
		if ( checkBlurLimited(kline.getHigh(), diffInHB, stockCode) ) {
			needRecordOpen = false;
			needRecordClose = false;
		}
		else if ( 
				checkBlurLimited(kline.getHigh(), diffInHT, stockCode) ) {
			if ( kline.getClose() > kline.getOpen() ) {
				needRecordClose = false;
			} else {
				needRecordOpen = false;
			}
		}
		
		// --- low check ---
		if ( checkBlurLimited(kline.getLow(), diffInLT, stockCode) ) {
			needRecordOpen = false;
			needRecordClose = false;
		}
		else if (
				checkBlurLimited(kline.getLow(), diffInLB, stockCode) ) {
			if ( kline.getClose() < kline.getOpen() ) {
				needRecordClose = false;
			} else {
				needRecordOpen = false;
			}
		}
		
		// --- close & open check ----
		if ( needRecordOpen && needRecordOpen
				&& StockBusinessHelper.checkDiffScope(kline.getOpen() , diffInOC) ) {
			needRecordClose = false;
		}
		
		// --- set the merge price ---
		if (needRecordHigh) {
			mergePrice.put( kline.getHigh() , "H");
		}
		
		if (needRecordOpen) {
			mergePrice.put( kline.getOpen(), "O");
		}
	
		if (needRecordClose) {
			mergePrice.put( kline.getClose(), "C");
		}

		if (needRecordLow) {
			mergePrice.put( kline.getLow(), "L");
		}

		
		return mergePrice;

	}


	// ----- defined the static default price ----
	/**
	 * get diff abs price value , please use StockBusinessHelperManager.getStockBlurLimitedPrice
	 * @param price
	 * @param type
	 * @return
	 */
	public static double getStockBlurLimitedPrice(double price, BlurRangeType type) {

		double diff = 0;
		double maxPrice = 0;
		double minPrice = 0;
		if ( price <= 6) {
			diff = price * 0.007;
		}
		else if (price > 6 && price <= 10) {
			diff = price * 0.005;
		}
		else {
			// different between 0 to 1
			diff = price * 0.003;
		}		
		maxPrice = price + diff;
		minPrice = price - diff;
		
		if (type == BlurRangeType.MAX) {
			return maxPrice;
		} else {
			return minPrice;
		}
		
	}
	
}
